package binary_evolution;

import java.util.Arrays;

import misc.not_Legal_Exception;
import evolution_superclasses.Evolutionary_Loop;
import evolution_superclasses.Genotype;
import evolution_superclasses.Phenotype;

public class TaskA extends Evolutionary_Loop{
	private Binary_Fitness_Evaluation _bfe;
	private Binary_Population _bpop;
	private Binary_Genetic_Operators _bgo;
	
	public TaskA(){
		super();
		set_plotList(new boolean[]{true,true,true,true});
	};
	
	public static void main(String[] args) throws Exception {
		TaskA a = new TaskA();
		boolean die = false;
		try {
			a.begin();
//			a.autotest_TaskA_Prob3();
		} catch (Exception e) {
			die = true;
			e.printStackTrace();
		}
		if(!die){
			try {
				System.out.println("Again? (1/0)");
				if(a._ih.readInt()==1){
					main(null);
					return;
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	public void autotest_TaskA_Prob3() throws Exception{
		int bestI = 300, needed;
		_plot = false;
		
//		search: for(int i = 150; i < 351; i+=5){
//			//Need work in 8 out of 10 time to be called consistent
//			needed = 8;
//			System.out.println("doh");
//			for(int j = 0; j < 10; j++){
//				initializeEndConditions(100);
//				initializePopulationSize(i);
//				initializeGenotypeSize(40);
//				initializeGeneticOperatiors(0.2,0.6);
//				initializeEliteism(0);
//				initializeFitnessEvaluation(0);
//				initializeAdultPoolSize(0);
//				initializeSelectionProtocol(0);
//				initializePopulation();
//				initializeSelectionMechanism(0);
//				
//				evolutionLoop();
//				if(_endBestFitness==1){
//					System.out.println("hope is here @"+i);
//					if(--needed <= 0){
//						System.out.println("solution at i= "+i);
//						bestI = i;
//						break search;
//					}
//				}
//			}
//		}
//		System.out.println("Consistently found at pop size "+bestI);
//		
////		0.01 0.9
//		//Initial values found by prievious runs
////		for(double m = 0.05; m < 1.01; m += 0.05){
////			for(double c = 0.05; c < 1.01; c += 0.05){
//		for(double m = 0.0; m < 0.036; m += 0.01){
//			for(double c = 0.7; c < 0.91; c += 0.05){
//					needed = 9;
//					for(int i = 0; i < 10; i++){
//						clearArrayLists();
//						
//						initializeEndConditions(100); //demand
//						initializePopulationSize(300);//prev test, can use bestI
//						initializeGenotypeSize(40); //demand
//						initializeGeneticOperatiors(m,c);
//						initializeEliteism(0); //not said = don't use
//						initializeFitnessEvaluation(0); //average (= number of ones/total)
//						initializeAdultPoolSize(0); //full-generation needs all the room
//						initializeSelectionProtocol(0); //full-generation 
//						initializePopulation();
//						initializeSelectionMechanism(0);//fitness-proportioned
//						
//						evolutionLoop();
//						
//						if(_endBestFitness==1){
//							if(--needed <= 0){//Make sure its consistently good enough
//								System.out.println("solution found at m= "+m+" and c= "+c);
//								break;
//							}
//						}
//					}
//			}
//		}
		
		double[] averageGererationNumber = {0,0,0,0};
		for(int i = 0; i < 4000; i++){
			clearArrayLists();
			
			initializeEndConditions(1000); //Let it run awhile
			initializePopulationSize(300);//prev test, can use bestI
			initializeGenotypeSize(40); //demand
			initializeGeneticOperatiors(0.01, 0.9);
			initializeEliteism(0); //not said = don't use
			initializeFitnessEvaluation(0); //average (= number of ones/total)
			initializeAdultPoolSize(0); //full-generation needs all the room
			initializeSelectionProtocol(0); //full-generation 
			initializePopulation();
			initializeSelectionMechanism(i%4);//all the different
			
			evolutionLoop();
			
			averageGererationNumber[i%4] += get_generationNumber();
		}
		
		System.out.println("Fitness-proportionate average generation: "+averageGererationNumber[0]/1000);
		System.out.println("Sigma-scaling average generation: "+averageGererationNumber[1]/1000);
		System.out.println("Rank selection average generation: "+averageGererationNumber[2]/1000);
		System.out.println("Tournement selection average generation: "+averageGererationNumber[3]/1000);
	}
	
//	@Override
//	public void begin() throws Exception{
//		clearArrayLists();
//		set_title("Random tournement selection");
//		set_filename("/A/RTS");
//		_plot = false;
//		
//		initializePrint(false);
//		initializeEndConditions(100);
//		initializePopulationSize(300);
//		initializeGenotypeSize(40);
//		initializeGeneticOperatiors(0.01, 0.9);
//		initializeEliteism(0);
//		initializeFitnessEvaluation(1);
//		initializeAdultPoolSize(0);
//		initializeSelectionProtocol(0);
//		initializePopulation();
//		initializeSelectionMechanism(3);
//		
//		evolutionLoop();
//		if(!_print)
//			printEndResults();
//	}

	@Override
	protected void createNewPopulation() {
		double best = get_bestFitness();
		
		if(_printEach){
			System.out.println("\nGeneration "+(get_generationNumber()-1)+"\nBest fitness: "+best);
			System.out.println("Total fitness: "+get_totalFitness());
		}
		
		if(best==1){
			finished();
			return;
		}
		
		int nr = 0, i = 0;
		Genotype[] tmp = new Genotype[_popSize];		

		while(i < _freepass){
			Binary_Genotype bg1 = new Binary_Genotype(_genSize);

			bg1.set_binaryArray(((Binary_Genotype)_bsp.get_adultPool()[i].get_genotype()).get_binaryArray().clone());
			tmp[i++] = bg1;
		}
		
		while(nr < _popSize-_freepass){
			Binary_Genotype bg1 = new Binary_Genotype(_genSize);
			Binary_Genotype bg2 = new Binary_Genotype(_genSize);

			bg1.set_binaryArray(((Binary_Genotype)_bsm.get_parents()[nr++].get_genotype()).get_binaryArray().clone());
			bg2.set_binaryArray(((Binary_Genotype)_bsm.get_parents()[((nr++)%_bsm.get_parents().length)].get_genotype()).get_binaryArray().clone());

			if( _crossOverRate > _rand.nextDouble()){
				_bgo.crossOver(bg1, bg2);
			}

			if( _mutationRate > _rand.nextDouble()){
				_bgo.mutation(bg1);
			}
			if( _mutationRate > _rand.nextDouble()){
				_bgo.mutation(bg2);
			}
			
			tmp[i++] = bg1;
			if(i<_popSize) //In case it's an odd number.
			tmp[i++] = bg2;	
		}
		
		_bpop.set_genotypes(tmp);		
	}

	@Override
	protected void initializeAndDevelopGeneration() throws not_Legal_Exception {
		int index = 0;
		double total = 0, tmp;
		_generation = new Binary_Phenotype[_popSize];
		
		for(Genotype g : _bpop.get_genotypes()){
			Binary_Genotype bg = (Binary_Genotype)g;
			Binary_Phenotype bp = (Binary_Phenotype)bg.generatePhenotype();
			tmp = _bfe.evaluateFitness(bp);
			total += tmp;
			bp.set_fitness(tmp);
			_generation[index++] = bp;
		}
		double avg = total/_popSize, variance = 0;
		for(Phenotype p : _generation){
    		variance += Math.pow(p.get_fitness()-avg,2);
    	}
    	double standardDev = Math.sqrt(variance/_popSize);
		
		Arrays.sort(_generation);

		set_bestFitness(_generation[0].get_fitness());
		set_bestInduvidual(_generation[0].get_genotype());
		set_totalFitness(total);
		set_worstFitness(_generation[_popSize-1].get_fitness());
		
		_max.add(_generation[0].get_fitness());
		_avg.add(get_totalFitness()/_popSize);
		_min.add(get_worstFitness());
		_stdDerivance.add(standardDev);
	}

	@Override
	protected void initializeFitnessEvaluation(int i) throws not_Legal_Exception, Exception {
		if(i>-1){
			_bfe = new Binary_Fitness_Evaluation(i, _print);
			return;
		}
		System.out.println("Now, what kind of fitness evaluation will you choose? (0 is average)");
		try {
			_bfe = new Binary_Fitness_Evaluation(_ih.readInt(),_print);
		} catch (Exception e) {
			System.out.println("Illegal value detected, try again");
			initializeFitnessEvaluation(i);
		}
	}

	@Override
	protected void initializeGeneticOperatiors(double m, double c) {
		if(m>-1){
			_mutationRate = m;
			_crossOverRate = c;
			_bgo = new Binary_Genetic_Operators(_genSize);
			return;
		}
		try {
			System.out.println("At what rate should genotypes mutate? (type double, between 0 and 1)");
			_mutationRate = _ih.readDouble();
			System.out.println("At what rate should genotypes suffer crossOvers? (type double, between 0 and 1)");
			_crossOverRate = _ih.readDouble();
			if(_mutationRate < 0 || _mutationRate > 1 || _crossOverRate < 0 || _crossOverRate > 1)
				throw new IllegalArgumentException();
		} catch (Exception e) {
			System.out.println("Illegal value detected, try again");
			initializeGeneticOperatiors(m,c);
			return;
		}
		_bgo = new Binary_Genetic_Operators(_genSize);
	}

	@Override
	protected void initializePopulation() {
		_bpop = new Binary_Population(_genSize, _popSize);
	}
}
